package cn.hdu.liang.ch6;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.partitioningBy;

public class PartPrimeNum {
    public static void main(String[] args) {

        Map<Boolean, List<Integer>> partPrimes = getPartPrimes(100);
        System.out.println(partPrimes);

        Map<Boolean, List<Integer>> partPrimesWithCustomCollector =
                getPrimesWithCustomCollector(100);
        System.out.println(partPrimesWithCustomCollector);

        Map<Boolean, List<Integer>> primesWithCustomCollector2 = getPrimesWithCustomCollector2(100);
        System.out.println(primesWithCustomCollector2);

    }

    public static Map<Boolean, List<Integer>> getPrimesWithCustomCollector2(int n) {
        return IntStream.rangeClosed(2, n).boxed()
                .collect(
                        () -> new HashMap<Boolean, List<Integer>>() {{
                            put(true, new ArrayList<>());
                            put(false, new ArrayList<>());
                        }},
                        (acc, can) ->
                                acc.get(isPrime(acc.get(true), can))
                                        .add(can),
                        (map1, map2) -> {
                            map1.get(true).addAll(map2.get(true));
                            map1.get(false).addAll(map2.get(false));
                        }
                );
    }


    public static Map<Boolean, List<Integer>> getPrimesWithCustomCollector(int n) {
        return IntStream.rangeClosed(2, n).boxed()
                .collect(new PrimeNumCollector());
    }

    public static Map<Boolean, List<Integer>> getPartPrimes(int n) {
        return IntStream.rangeClosed(2, n)
                .boxed()
                .collect(partitioningBy(PartPrimeNum::isPrime));
    }

    public static boolean isPrime(int candidate) {
        int canRoot = (int) Math.sqrt((double) candidate);
        return IntStream.rangeClosed(2, canRoot)
                .noneMatch(i -> candidate % i == 0);
    }

    public static boolean isPrime(List<Integer> primes, int candidate) {
        //return primes.stream().noneMatch(i -> candidate % i == 0);
        int canRoot = (int) Math.sqrt((double) candidate);
        return takeWhile(primes, i -> i <= canRoot)
                .stream().noneMatch(p -> candidate % p == 0);
    }

    public static <A> List<A> takeWhile(List<A> list, Predicate<A> p) {
        int i = 0;
        for(A item : list) {
            if(!p.test(item)) {
                return list.subList(0, i);
            }
            i++;
        }
        return list;
    }

}
